package com.gitee.sidihuo.platform.base;

import com.alibaba.fastjson.JSONObject;
import com.gitee.sidihuo.platform.annotation.ApiControllerScene;
import com.gitee.sidihuo.platform.annotation.LogPrintForbid;
import com.gitee.sidihuo.platform.model.ApiResult;
import com.gitee.sidihuo.platform.model.LogAspectPara;
import com.gitee.sidihuo.platform.utils.EnvHelper;
import com.gitee.sidihuo.utils.base.BaseForm;
import com.gitee.sidihuo.utils.base.BasePageForm;
import com.gitee.sidihuo.utils.base.IBaseForm;
import com.gitee.sidihuo.utils.exception.AssertException;
import com.gitee.sidihuo.utils.util.AssertUtils;
import com.gitee.sidihuo.utils.util.IdGenerator;
import com.gitee.sidihuo.utils.util.MdcUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Slf4j
public class BaseLogAspect {

    public Object aroundController(ProceedingJoinPoint point) throws Throwable {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        Throwable throwable = null;
        String uri = null;
        Object proceedResult = null;
        try {

            //======================================================== begin pre
            try {
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = attributes.getRequest();
                uri = request.getRequestURI();
                validateUri(request);
                handleForm(point, uri);
            } catch (Throwable t) {
                if (t instanceof AssertException) {
                    throw t;
                }
                log.warn("ControllerAspect begin pre Throwable [保护主流程] ", t);
            }
            //======================================================== end pre

            proceedResult = point.proceed();
            return proceedResult;
        } catch (Throwable e) {
            log.warn("【ControllerAspectThrowable】 throwableClassName = {}", e.getClass().getName());
            throwable = e;
            throw e;
        } finally {
            //======================================================== begin post
            try {
                stopWatch.stop();
                long timeConsuming = stopWatch.getTotalTimeMillis();
                if (throwable == null) {
                    printLogVo(proceedResult, uri, timeConsuming);
                } else {
                    log.warn("【ControllerAspectFailed】 url = {} timeConsuming = {}", uri, timeConsuming, throwable);
                }
                if (timeConsuming > 2000) {
                    log.info("long-url-2000 {} {}", uri, timeConsuming);
                } else if (timeConsuming > 1000) {
                    log.info("long-url-1000 {} {}", uri, timeConsuming);
                } else if (timeConsuming > 500) {
                    log.info("long-url-500 {} {}", uri, timeConsuming);
                }

                LogAspectPara logAspectPara = new LogAspectPara();
                Class<?> targetClass = point.getTarget().getClass();
                if (targetClass.isAnnotationPresent(ApiControllerScene.class)) {
                    ApiControllerScene annotation = targetClass.getAnnotation(ApiControllerScene.class);
                    String apiControllerScene = annotation.value();
                    logAspectPara.setApiControllerScene(apiControllerScene);
                }

                finalHandle(uri, timeConsuming, throwable, logAspectPara);

            } catch (Throwable t) {
                log.warn("finally [保护主流程] ", t);
            }
            //MdcUtils.removeLogTrace();
            //======================================================== end post
        }

    }

    protected void finalHandle(String url, long timeConsuming, Throwable throwable, LogAspectPara logAspectPara) {

    }

    protected void handleForm(ProceedingJoinPoint point, String url) {
        BaseForm form = null;
        String requestNo = null;
        Object[] args = point.getArgs();
        if (args != null) {
            for (Object arg : args) {
                if (arg instanceof BaseForm) {
                    form = (BaseForm) arg;
                    requestNo = (form).getRequestNo();
                    AssertUtils.assertRestFormLegal(StringUtils.isNotBlank(requestNo), "接口入参缺少参数值 requestNo", url);
                    if (requestNo.length() > 6) {
                        requestNo = requestNo.substring(0, 6);
                        form.setRequestNo(requestNo);
                    }
                    //MdcUtils.setLogTrace(requestNo);
                    validatePageForm(form);
                }
            }
        }
        if (StringUtils.isBlank(requestNo)) {
            requestNo = IdGenerator.random6();
            //MdcUtils.setLogTrace(requestNo);
        }
        setLogTrace(requestNo);
        printLogForm(url, form, requestNo);
    }

    protected void setLogTrace(String requestNo) {
        MdcUtils.setLogTrace(requestNo);
    }

    protected void validateUri(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        boolean legalUri = true;
        AssertUtils.assertContextParaLegal(legalUri, "接口URL定义不符合规范", "参考URL拦截配置 " + requestURI);
        if (EnvHelper.ISPRD) {
            AssertUtils.assertContextParaLegal(true, "非生产环境接口", requestURI);
        }
    }

    protected void validatePageForm(IBaseForm form) {
        if (!(form instanceof BasePageForm)) {
            return;
        }
        BasePageForm pageForm = (BasePageForm) form;
        if (pageForm.getPageIndex() < 1) {
            pageForm.setPageIndex(1);
        }
        if (pageForm.getPageSize() < 1) {
            pageForm.setPageIndex(10);
        }
    }

    protected void printLogForm(String url, BaseForm form, String requestNo) {
        if (form == null) {
            log.info("【ControllerAspectBegin】 requestNo = {} url = {} form = {}", requestNo, url, "not extends BaseForm");
            return;
        }
        boolean logPrintForbid = form.getClass().isAnnotationPresent(LogPrintForbid.class);
        if (logPrintForbid) {
            log.info("【ControllerAspectBegin】 requestNo = {} url = {} form = {}", requestNo, url, "logPrintForbid");
        } else {
            log.info("【ControllerAspectBegin】 requestNo = {} url = {} form = {}", requestNo, url, JSONObject.toJSONString(form));
        }
    }

    protected void printLogVo(Object proceedResult, String url, long timeConsuming) {
        boolean isApiResult = proceedResult != null && (proceedResult instanceof ApiResult);
        if (!isApiResult) {
            log.info("【ControllerAspectSuccess】 url = {} timeConsuming = {} \nproceedResult ※※※※※※ = {}", url, timeConsuming, JSONObject.toJSONString(proceedResult));
            return;
        }
        ApiResult apiResult = (ApiResult) proceedResult;
        Object apiResultData = apiResult.getData();
        boolean logPrintForbid = apiResultData != null && apiResultData.getClass().isAnnotationPresent(LogPrintForbid.class);
        if (logPrintForbid) {
            log.info("【ControllerAspectSuccess】 url = {} timeConsuming = {} \nproceedResult ※※※※※※ = {} {}", url, timeConsuming, "logPrintForbid", apiResult.getRequestNo());
        } else {
            log.info("【ControllerAspectSuccess】 url = {} timeConsuming = {} \nproceedResult ※※※※※※ = {}", url, timeConsuming, JSONObject.toJSONString(proceedResult));
        }
    }

}
